/*
 * Mkl.cs
 *
 * LAPACK provider for INTEL's MKL.
 *
 * Copyright (c) 2005, dnAnalytics. All rights reserved.
 */

#region Using Directives
using dnAnalytics;
using dnAnalytics.Math;
using dnAnalytics.LinearAlgebra.Native;
#endregion

namespace dnAnalytics.LinearAlgebra.Native.Mkl
{
    /// <summary>
    /// Summary description for Mkl Lapack.
    /// </summary>
    internal sealed class Lapack : ILapack
    {
        private static readonly ILapack instance = new Lapack();

        private Lapack() { }

        #region Public Members

        public static ILapack Instance
        {
            get { return instance; }
        }

        public int Dgeqp3(int m, int n, double[] a, int lda, int[] jpvt, double[] tau)
        {
            double[] work = new double[System.Math.Max(1, 3 * n + 1)];
            return Dgeqp3(m, n, a, lda, jpvt, tau, work);
        }

        public int Dgeqp3(int m, int n, double[] a, int lda, int[] jpvt, double[] tau, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.DGEQP3(ref m, ref n, a, ref lda, jpvt, tau, work, ref len, ref info);
            return info;
        }

        public int Dgesvd(char jobu, char jobvt, int m, int n, double[] a, int lda, double[] s, double[] u, int ldu, double[] vt, int ldvt)
        {
            double[] work = new double[System.Math.Max((3 * System.Math.Min(m, n) + System.Math.Max(m, n)), 5 * System.Math.Min(m, n))];
            return Dgesvd(jobu, jobvt, m, n, a, lda, s, u, ldu, vt, ldvt, work);
        }

        public int Dgesvd(char jobu, char jobvt, int m, int n, double[] a, int lda, double[] s, double[] u, int ldu, double[] vt, int ldvt, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.DGESVD(ref jobu, ref jobvt, ref m, ref n, a, ref lda, s, u, ref ldu, vt, ref ldvt, work, ref len, ref info);
            return info;
        }

        public int Dgetrf(int m, int n, double[] a, int lda, out int[] ipiv)
        {
            int info = 0;
            ipiv = new int[m];
            SafeNativeMethods.DGETRF(ref m, ref n, a, ref lda, ipiv, ref info);
            return info;
        }

        public int Dgetri(int n, double[] a, int lda, int[] ipiv)
        {
            int bs = Ilaenv(1, "DGETRI", "", n, -1, -1, -1);
            double[] work = new double[n * bs];
            return Dgetri(n, a, lda, ipiv, work);
        }

        public int Dgetri(int n, double[] a, int lda, int[] ipiv, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.DGETRI(ref n, a, ref lda, ipiv, work, ref len, ref info);
            return info;
        }

        public int Dgetrs(char trans, int n, int nrhs, double[] a, int lda, int[] ipiv, double[] b, int ldb)
        {
            int info = 0;
            SafeNativeMethods.DGETRS(ref trans, ref n, ref nrhs, a, ref lda, ipiv, b, ref ldb, ref info);
            return info;
        }

        public int Dorgqr(int m, int n, int k, double[] a, int lda, double[] tau)
        {
            int bs = Ilaenv(1, "DORGQR", "", m, n, k, -1);
            double[] work = new double[n * bs];
            return Dorgqr(m, n, k, a, lda, tau, work);
        }

        public int Dorgqr(int m, int n, int k, double[] a, int lda, double[] tau, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.DORGQR(ref m, ref n, ref k, a, ref lda, tau, work, ref len, ref info);
            return info;
        }

        public int Dormqr(char side, char trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc)
        {
            string opts = side.ToString() + trans.ToString();
            int bs = Ilaenv(1, "DORMQR", opts, m, n, k, -1);
            double[] work = null;
            if (side == 'L' || side == 'l')
            {
                work = new double[n * bs];
            }
            else
            {
                work = new double[m * bs];
            }
            return Dormqr(side, trans, m, n, k, a, lda, tau, c, ldc, work);
        }

        public int Dormqr(char side, char trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.DORMQR(ref side, ref trans, ref m, ref n, ref k, a, ref lda, tau, c, ref ldc, work, ref len, ref info);
            return info;
        }

        public int Dpotrf(char uplo, int n, double[] a, int lda)
        {
            int info = 0;
            SafeNativeMethods.DPOTRF(ref uplo, ref n, a, ref lda, ref info);
            return info;
        }

        public int Dpotri(char uplo, int n, double[] a, int lda)
        {
            int info = 0;
            SafeNativeMethods.DPOTRI(ref uplo, ref n, a, ref lda, ref info);
            return info;
        }

        public int Dpotrs(char uplo, int n, int nrhs, double[] a, int lda, double[] b, int ldb)
        {
            int info = 0;
            SafeNativeMethods.DPOTRS(ref uplo, ref n, ref nrhs, a, ref lda, b, ref ldb, ref info);
            return info;
        }

        public int Zgeqp3(int m, int n, Complex[] a, int lda, int[] jpvt, Complex[] tau)
        {
            Complex[] work = new Complex[System.Math.Max(1, n + 1)];
            double[] rwork = new double[System.Math.Max(1, 2 * n)];
            return Zgeqp3(m, n, a, lda, jpvt, tau, work, rwork);
        }

        public int Zgeqp3(int m, int n, Complex[] a, int lda, int[] jpvt, Complex[] tau, Complex[] work, double[] rwork)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.ZGEQP3(ref m, ref n, a, ref lda, jpvt, tau, work, ref len, rwork, ref info);
            return info;
        }

        public int Zgetrf(int m, int n, Complex[] a, int lda, out int[] ipiv)
        {
            int info = 0;
            ipiv = new int[m];
            SafeNativeMethods.ZGETRF(ref m, ref n, a, ref lda, ipiv, ref info);
            return info;
        }

        public int Zgetri(int n, Complex[] a, int lda, int[] ipiv)
        {
            int bs = Ilaenv(1, "ZGETRI", "", n, -1, -1, -1);
            Complex[] work = new Complex[n * bs];
            return Zgetri(n, a, lda, ipiv, work);
        }

        public int Zgetri(int n, Complex[] a, int lda, int[] ipiv, Complex[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.ZGETRI(ref n, a, ref lda, ipiv, work, ref len, ref info);
            return info;
        }

        public int Zgetrs(char trans, int n, int nrhs, Complex[] a, int lda, int[] ipiv, Complex[] b, int ldb)
        {
            int info = 0;
            SafeNativeMethods.ZGETRS(ref trans, ref n, ref nrhs, a, ref lda, ipiv, b, ref ldb, ref info);
            return info;
        }

        public int Zpotrf(char uplo, int n, Complex[] a, int lda)
        {
            int info = 0;
            SafeNativeMethods.ZPOTRF(ref uplo, ref n, a, ref lda, ref info);
            return info;
        }

        public int Zpotri(char uplo, int n, Complex[] a, int lda)
        {
            int info = 0;
            SafeNativeMethods.ZPOTRI(ref uplo, ref n, a, ref lda, ref info);
            return info;
        }

        public int Zpotrs(char uplo, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb)
        {
            int info = 0;
            SafeNativeMethods.ZPOTRS(ref uplo, ref n, ref nrhs, a, ref lda, b, ref ldb, ref info);
            return info;
        }

        public int Zungqr(int m, int n, int k, Complex[] a, int lda, Complex[] tau)
        {
            int bs = Ilaenv(1, "ZUNGQR", "", m, n, k, -1);
            Complex[] work = new Complex[n * bs];
            return Zungqr(m, n, k, a, lda, tau, work);
        }

        public int Zungqr(int m, int n, int k, Complex[] a, int lda, Complex[] tau, Complex[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.ZUNGQR(ref m, ref n, ref k, a, ref lda, tau, work, ref len, ref info);
            return info;
        }

        public int Zunmqr(char side, char trans, int m, int n, int k, Complex[] a, int lda, Complex[] tau, Complex[] c, int ldc)
        {
            string opts = side.ToString() + trans.ToString();
            int bs = Ilaenv(1, "ZUNMQR", opts, m, n, k, -1);
            Complex[] work = null;
            if (side == 'L' || side == 'l')
            {
                work = new Complex[n * bs];
            }
            else
            {
                work = new Complex[m * bs];
            }
            return Zunmqr(side, trans, m, n, k, a, lda, tau, c, ldc, work);
        }

        public int Zunmqr(char side, char trans, int m, int n, int k, Complex[] a, int lda, Complex[] tau, Complex[] c, int ldc, Complex[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.ZUNMQR(ref side, ref trans, ref m, ref n, ref k, a, ref lda, tau, c, ref ldc, work, ref len, ref info);
            return info;
        }

        public int Zgesvd(char jobu, char jobvt, int m, int n, Complex[] a, int lda, double[] s, Complex[] u, int ldu, Complex[] vt, int ldvt)
        {
            Complex[] work = new Complex[2 * System.Math.Min(m, n) + System.Math.Max(m, n)];
            double[] rwork = new double[System.Math.Max(1, 5 * System.Math.Min(m, n))];
            return Zgesvd(jobu, jobvt, m, n, a, lda, s, u, ldu, vt, ldvt, work, rwork);
        }

        public int Zgesvd(char jobu, char jobvt, int m, int n, Complex[] a, int lda, double[] s, Complex[] u, int ldu, Complex[] vt, int ldvt, Complex[] work, double[] rwork)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.ZGESVD(ref jobu, ref jobvt, ref m, ref n, a, ref lda, s, u, ref ldu, vt, ref ldvt, work, ref len, rwork, ref info);
            return info;
        }

        public int Dgels(char trans, int m, int n, int nrhs, double[] a, int lda, double[] b, int ldb)
        {
            int bs = Ilaenv(1, "DGELS", trans.ToString(), m, n, nrhs, -1);
            double[] work = new double[System.Math.Min(m, n) + System.Math.Max(m, System.Math.Max(n, nrhs)) * bs];
            return Dgels(trans, m, n, nrhs, a, lda, b, ldb, work);
        }

        public int Dgels(char trans, int m, int n, int nrhs, double[] a, int lda, double[] b, int ldb, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.DGELS(ref trans, ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, work, ref len, ref info);
            return info;
        }

        public int Zgels(char trans, int m, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb)
        {
            int bs = Ilaenv(1, "ZGELS", trans.ToString(), m, n, nrhs, -1);
            Complex[] work = new Complex[System.Math.Min(m, n) + System.Math.Max(m, System.Math.Max(n, nrhs)) * bs];
            return Zgels(trans, m, n, nrhs, a, lda, b, ldb, work);
        }

        public int Zgels(char trans, int m, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb, Complex[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.ZGELS(ref trans, ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, work, ref len, ref info);
            return info;
        }

        public int Dgelss(int m, int n, int nrhs, double[] a, int lda, double[] b, int ldb, out double[] s, ref int rank)
        {
            double[] work = new double[3 * System.Math.Min(m, n) + System.Math.Max(2 * System.Math.Min(m, n), System.Math.Max(System.Math.Max(m, n), nrhs))];
            double rcond = -1;
            return Dgelss(m, n, nrhs, a, lda, b, ldb, out s, rcond, ref rank, work);
        }

        public int Dgelss(int m, int n, int nrhs, double[] a, int lda, double[] b, int ldb, out double[] s, double rcond, ref int rank, double[] work)
        {
            int info = 0;
            int len = work.Length;
            s = new double[System.Math.Min(m, n)];
            SafeNativeMethods.DGELSS(ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, s, ref rcond, ref rank, work, ref len, ref info);
            return info;
        }

        public int Zgelss(int m, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb, out double[] s, ref int rank)
        {
            Complex[] work = new Complex[3 * System.Math.Min(m, n) + System.Math.Max(2 * System.Math.Min(m, n), System.Math.Max(System.Math.Max(m, n), nrhs))];
            double[] rwork = new double[System.Math.Max(1, 5 * System.Math.Min(m, n))];
            double rcond = -1;
            return Zgelss(m, n, nrhs, a, lda, b, ldb, out s, rcond, ref rank, work, rwork);
        }

        public int Zgelss(int m, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb, out double[] s, double rcond, ref int rank, Complex[] work, double[] rwork)
        {
            int info = 0;
            int len = work.Length;
            s = new double[System.Math.Min(m, n)];
            SafeNativeMethods.ZGELSS(ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, s, ref rcond, ref rank, work, ref len, rwork, ref info);
            return info;
        }

        public int Dgelsy(int m, int n, int nrhs, double[] a, int lda, double[] b, int ldb, int[] jpvt, double rcond, ref int rank)
        {
            int nb = Ilaenv(1, "DGELSY", "", m, n, nrhs, -1);
            int mn = System.Math.Min(m, n);
            int len = System.Math.Max((mn + 2) * n + nb * (n + 1), 2 * mn + nb * nrhs);
            double[] work = new double[len];
            return Dgelsy(m, n, nrhs, a, lda, b, ldb, jpvt, rcond, ref rank, work);
        }

        public int Dgelsy(int m, int n, int nrhs, double[] a, int lda, double[] b, int ldb, int[] jpvt, double rcond, ref int rank, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.DGELSY(ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, jpvt, ref rcond, ref rank, work, ref len, ref info);
            return info;
        }

        public int Zgelsy(int m, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb, int[] jpvt, double rcond, ref int rank)
        {
            int nb = Ilaenv(1, "ZGELSY", "", m, n, nrhs, -1);
            int mn = System.Math.Min(m, n);
            int len = mn + System.Math.Max(System.Math.Max(System.Math.Max(2 * mn, nb * (n + 1)), mn + mn * nb), mn + nb * nrhs);
            Complex[] work = new Complex[len];
            double[] rwork = new double[System.Math.Max(1, 2 * n)];
            return Zgelsy(m, n, nrhs, a, lda, b, ldb, jpvt, rcond, ref rank, work, rwork);
        }

        public int Zgelsy(int m, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb, int[] jpvt, double rcond, ref int rank, Complex[] work, double[] rwork)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.ZGELSY(ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, jpvt, ref rcond, ref rank, work, ref len, rwork, ref info);
            return info;
        }

        public double Dlange(char norm, int m, int n, double[] a, int lda)
        {
            double[] work = new double[m];
            return Dlange(norm, m, n, a, lda, work);
        }

        public double Dlange(char norm, int m, int n, double[] a, int lda, double[] work)
        {
            return SafeNativeMethods.DLANGE(ref norm, ref m, ref n, a, ref lda, work);
        }

        public double Zlange(char norm, int m, int n, Complex[] a, int lda)
        {
            double[] work = new double[m];
            return Zlange(norm, m, n, a, lda, work);
        }

        public double Zlange(char norm, int m, int n, Complex[] a, int lda, double[] work)
        {
            return SafeNativeMethods.ZLANGE(ref norm, ref m, ref n, a, ref lda, work);
        }

        public int Dgecon(char norm, int n, double[] a, int lda, double anorm, ref double rcond)
        {
            double[] work = new double[System.Math.Max(1, 4 * n)];
            int[] iwork = new int[System.Math.Max(1, n)];
            return Dgecon(norm, n, a, lda, anorm, ref rcond, work, iwork);
        }

        public int Dgecon(char norm, int n, double[] a, int lda, double anorm, ref double rcond, double[] work, int[] iwork)
        {
            int info = 0;
            SafeNativeMethods.DGECON(ref norm, ref n, a, ref lda, ref anorm, ref rcond, work, iwork, ref info);
            return info;
        }

        public int Zgecon(char norm, int n, Complex[] a, int lda, double anorm, ref double rcond)
        {
            Complex[] work = new Complex[System.Math.Max(1, 4 * n)];
            double[] rwork = new double[System.Math.Max(1, 2 * n)];
            return Zgecon(norm, n, a, lda, anorm, ref rcond, work, rwork);
        }

        public int Zgecon(char norm, int n, Complex[] a, int lda, double anorm, ref double rcond, Complex[] work, double[] rwork)
        {
            int info = 0;
            SafeNativeMethods.ZGECON(ref norm, ref n, a, ref lda, ref anorm, ref rcond, work, rwork, ref info);
            return info;
        }

        public int Ilaenv(int ispec, string name, string opts, int n1, int n2, int n3, int n4)
        {
            short name_len = (short)name.Length;
            short opts_len = (short)opts.Length;
            return SafeNativeMethods.ilaenv(ref ispec, name, opts, ref n1, ref n2, ref n3, ref n4, ref name_len, ref opts_len);
        }

        public int Dgeqrf(int m, int n, double[] a, int lda, double[] tau)
        {
            int nb = Ilaenv(1, "DGEQRF", "", m, n, -1, -1);
            double[] work = new double[n * nb];
            return Dgeqrf(m, n, a, lda, tau, work);
        }

        public int Dgeqrf(int m, int n, double[] a, int lda, double[] tau, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.DGEQRF(ref m, ref n, a, ref lda, tau, work, ref len, ref info);
            return info;
        }

        public int Zgeqrf(int m, int n, Complex[] a, int lda, Complex[] tau)
        {
            int nb = Ilaenv(1, "ZGEQRF", "", m, n, -1, -1);
            Complex[] work = new Complex[n * nb];
            return Zgeqrf(m, n, a, lda, tau, work);
        }

        public int Zgeqrf(int m, int n, Complex[] a, int lda, Complex[] tau, Complex[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.ZGEQRF(ref m, ref n, a, ref lda, tau, work, ref len, ref info);
            return info;
        }

        #endregion Public Members
    }
}